﻿using System;
using System.Linq;
using System.Data;
using System.Data.Entity;
using System.Linq.Expressions;
using System.Collections.Generic;
using System.Data.Entity.Infrastructure;
using Rainr.EFClient.Utilites;

namespace Rainr.EFClient
{
    /// <summary>
    /// 数据访问者
    /// </summary>
    /// <typeparam name="TContext">数据上下文类型</typeparam>
    public class DbAccessor<TContext> : DbAccessorBase<TContext>
        where TContext : DbContext, new()
    {
        /// <summary>
        /// 构造函数
        /// </summary>
        public DbAccessor() : base() { }

        /// <summary>
        /// 查询单个实体对象根据主键。
        /// </summary>
        /// <typeparam name="TEntity">实体对象类型</typeparam>
        /// <param name="keyValues">主键值集合[支持联合主键]</param>
        /// <returns>查询到的单个实体对象，查询失败则返回NULL</returns>
        public virtual TEntity SelectByKey<TEntity>(params object[] keyValues) where TEntity : class
        {
            return this.Context.Set<TEntity>().Find(keyValues);
        }
        /// <summary>
        /// 查询单个实体对象根据查询条件。
        /// </summary>
        /// <typeparam name="TEntity">实体对象类型</typeparam>
        /// <param name="where">查询条件</param>
        /// <returns>查询到的单个实体对象，查询失败则返回NULL</returns>
        public virtual TEntity Select<TEntity>(Expression<Func<TEntity, bool>> where) where TEntity : class
        {
            return this.Context.Set<TEntity>().Where(where).SingleOrDefault();
        }
        /// <summary>
        /// 查询单个元素对象根据SQL语句。
        /// </summary>
        /// <typeparam name="TElement">元素对象类型</typeparam>
        /// <param name="sql">SQL语句</param>
        /// <param name="parameters">SQL参数</param>
        /// <returns>元素对象</returns>
        public virtual TElement Select<TElement>(string sql, params object[] parameters)
        {
            return this.Context.Database.SqlQuery<TElement>(sql, parameters).SingleOrDefault<TElement>();
        }

        /// <summary>
        /// 查询多个实体对象。
        /// </summary>
        /// <typeparam name="TEntity">实体对象类型</typeparam>
        /// <returns>所有实体对象集合</returns>
        public virtual IQueryable<TEntity> Selects<TEntity>() where TEntity : class
        {
            Expression<Func<TEntity, bool>> func = this.SearchFilter<TEntity>(null);
            if (null != func)
            {
                return this.Context.Set<TEntity>().Where(func);
            }
            return this.Context.Set<TEntity>();
        }
        /// <summary>
        /// 查询多个实体对象根据查询条件。
        /// </summary>
        /// <typeparam name="TEntity">实体对象类型</typeparam>
        /// <param name="where">查询条件</param>
        /// <returns>实体对象集合</returns>
        public virtual IQueryable<TEntity> Selects<TEntity>(Expression<Func<TEntity, bool>> where) where TEntity : class
        {
            return this.Context.Set<TEntity>().Where(this.SearchFilter<TEntity>(where));
        }
        /// <summary>
        /// 查询多个元素对象。
        /// </summary>
        /// <typeparam name="TElement">元素对象类型</typeparam>
        /// <param name="sql">SQL语句</param>
        /// <param name="parameters">SQL参数</param>
        /// <returns>元素对象集合</returns>
        public virtual IEnumerable<TElement> Selects<TElement>(string sql, params object[] parameters)
        {
            Expression<Func<TElement, bool>> func = this.SearchFilter<TElement>(null);
            if (null != func)
            {
                return this.Context.Database.SqlQuery<TElement>(sql, parameters).Where(func.Compile());
            }
            return this.Context.Database.SqlQuery<TElement>(sql, parameters);
        }

        /// <summary>
        /// 更新指定对象属性。
        /// </summary>
        /// <typeparam name="TEntity">对象类型</typeparam>
        /// <param name="entity">对象</param>
        /// <param name="propertyNames">需要更新的对象属性集合</param>
        public virtual void UpdateProperty<TEntity>(TEntity entity, params string[] propertyNames) where TEntity : class
        {
            if (!DbAccessorUtility.IsProxyEntity(entity))
            {
                entity = DbAccessorUtility.ReplaceEntity<TEntity>(entity, this.Context);
            }
            this.Context.GetObjectContext().ObjectStateManager.ChangeObjectState(entity, EntityState.Unchanged);
            DbEntityEntry<TEntity> entityEntry = this.Context.Entry<TEntity>(entity);
            foreach (string propertyName in propertyNames)
            {
                entityEntry.Property(propertyName).IsModified = true;
            }
            try
            {
                this.SaveChanges();
            }
            catch (Exception)
            {
                DbAccessorUtility.RestoreEntityState(this.Context, EntityState.Modified);
                throw;
            }
            try
            {
                this.ChangedFilter<TEntity>(entity, EntityState.Modified);
            }
            catch (Exception)
            {
                DbAccessorUtility.RestoreEntityState(this.Context);
                throw;
            }
        }
        /// <summary>
        /// 更新排除指定属性外的其他对象属性。
        /// </summary>
        /// <typeparam name="TEntity">对象类型</typeparam>
        /// <param name="entity">对象</param>
        /// <param name="propertyNames">排除的属性集合</param>
        public virtual void UpdatePropertyExcluded<TEntity>(TEntity entity, params string[] propertyNames) where TEntity : class
        {
            if (!DbAccessorUtility.IsProxyEntity(entity))
            {
                entity = DbAccessorUtility.ReplaceEntity<TEntity>(entity, this.Context);
            }
            this.Context.GetObjectContext().ObjectStateManager.ChangeObjectState(entity, EntityState.Unchanged);
            DbEntityEntry<TEntity> entityEntry = this.Context.Entry<TEntity>(entity);
            foreach (string propertyName in entityEntry.CurrentValues.PropertyNames)
            {
                if (!propertyNames.Contains(propertyName))
                {
                    entityEntry.Property(propertyName).IsModified = true;
                }
            }
            try
            {
                this.SaveChanges();
            }
            catch (Exception)
            {
                DbAccessorUtility.RestoreEntityState(this.Context, EntityState.Modified);
                throw;
            }
            try
            {
                this.ChangedFilter<TEntity>(entity, EntityState.Modified);
            }
            catch (Exception)
            {
                DbAccessorUtility.RestoreEntityState(this.Context);
                throw;
            }
        }

        /// <summary>
        /// 添加实体对象。
        /// </summary>
        /// <typeparam name="TEntity">实体对象类型</typeparam>
        /// <param name="entity">实体对象</param>
        public virtual void Insert<TEntity>(TEntity entity) where TEntity : class
        {
            this.Context.Set<TEntity>().Add(entity);
            try
            {
                this.SaveChanges();
            }
            catch (Exception)
            {
                DbAccessorUtility.RestoreEntityState(this.Context, EntityState.Added);
                throw;
            }
            try
            {
                this.ChangedFilter<TEntity>(entity, EntityState.Added);
            }
            catch (Exception)
            {
                DbAccessorUtility.RestoreEntityState(this.Context);
                throw;
            }
        }
        /// <summary>
        /// 修改实体对象。
        /// </summary>
        /// <typeparam name="TEntity">实体对象类型</typeparam>
        /// <param name="entity">实体对象</param>
        public virtual void Update<TEntity>(TEntity entity) where TEntity : class
        {
            if (!DbAccessorUtility.IsProxyEntity(entity))
            {
                entity = DbAccessorUtility.ReplaceEntity<TEntity>(entity, this.Context);
            }
            this.Context.Entry<TEntity>(entity).State = EntityState.Modified;
            try
            {
                this.SaveChanges();
            }
            catch (Exception)
            {
                DbAccessorUtility.RestoreEntityState(this.Context, EntityState.Modified);
                throw;
            }
            try
            {
                this.ChangedFilter<TEntity>(entity, EntityState.Modified);
            }
            catch (Exception)
            {
                DbAccessorUtility.RestoreEntityState(this.Context);
                throw;
            }
        }
        /// <summary>
        /// 删除实体对象根据主键。
        /// </summary>
        /// <typeparam name="TEntity">实体对象类型</typeparam>
        /// <param name="keyValues">主键值集合[支持联合主键]</param>
        public virtual void DeleteByKey<TEntity>(params object[] keyValues) where TEntity : class
        {
            TEntity entity = this.SelectByKey<TEntity>(keyValues);
            this.Context.Set<TEntity>().Remove(entity);
            try
            {
                this.SaveChanges();
            }
            catch (Exception)
            {
                DbAccessorUtility.RestoreEntityState(this.Context, EntityState.Deleted);
                throw;
            }
            try
            {
                this.ChangedFilter<TEntity>(entity, EntityState.Deleted);
            }
            catch (Exception)
            {
                DbAccessorUtility.RestoreEntityState(this.Context);
                throw;
            }
        }
        /// <summary>
        /// 删除实体对象。使用此方法时请确保传入的实体对象是数据库查询所得，而非自己新建的对象。
        /// </summary>
        /// <typeparam name="TEntity">实体对象类型</typeparam>
        /// <param name="entity">实体对象</param>
        public virtual void Delete<TEntity>(TEntity entity) where TEntity : class
        {
            entity = DbAccessorUtility.GetProxyEntity<TEntity>(entity, this.Context);
            this.Context.Set<TEntity>().Remove(entity);
            try
            {
                this.SaveChanges();
            }
            catch (Exception)
            {
                DbAccessorUtility.RestoreEntityState(this.Context, EntityState.Deleted);
                throw;
            }
            try
            {
                this.ChangedFilter<TEntity>(entity, EntityState.Deleted);
            }
            catch (Exception)
            {
                DbAccessorUtility.RestoreEntityState(this.Context);
                throw;
            }
        }

        /// <summary>
        /// 执行SQL语句
        /// </summary>
        /// <param name="sql">SQL语句</param>
        /// <param name="parameters">SQL参数</param>
        /// <returns>受影响行数</returns>
        public virtual int Command(string sql, params object[] parameters)
        {
            return this.Context.Database.ExecuteSqlCommand(sql, parameters);
        }

        /// <summary>
        /// 查询多对象时表达式过滤。此方法会应用于同时执行多个对象的查询方法（Selects）。
        /// </summary>
        /// <typeparam name="T">对象或元素类型</typeparam>
        /// <param name="where">查询方法中的原始查询条件</param>
        /// <returns>一个新的查询过滤条件，此条件应包含原始方法中的过滤条件逻辑。</returns>
        protected virtual Expression<Func<T, bool>> SearchFilter<T>(Expression<Func<T, bool>> where)
        {
            return where;
        }
        /// <summary>
        /// 执行后过滤。此方法应用于实体对象的Insert/Update/Delete。它会在成功数据库访问操作后执行。
        /// </summary>
        /// <typeparam name="TEntity">实体对象类型</typeparam>
        protected virtual void ChangedFilter<TEntity>(TEntity entity, EntityState state) where TEntity : class
        {
        }
    }
}
